package cn.tedu.mall.seckill.service.impl;

import cn.tedu.mall.common.exception.CoolSharkServiceException;
import cn.tedu.mall.common.restful.JsonPage;
import cn.tedu.mall.common.restful.ResponseCode;
import cn.tedu.mall.pojo.product.vo.SpuDetailStandardVO;
import cn.tedu.mall.pojo.product.vo.SpuStandardVO;
import cn.tedu.mall.pojo.seckill.model.SeckillSpu;
import cn.tedu.mall.pojo.seckill.vo.SeckillSpuDetailSimpleVO;
import cn.tedu.mall.pojo.seckill.vo.SeckillSpuVO;
import cn.tedu.mall.product.service.seckill.IForSeckillSpuService;
import cn.tedu.mall.seckill.mapper.SeckillSpuMapper;
import cn.tedu.mall.seckill.service.ISeckillSpuService;
import cn.tedu.mall.seckill.utils.SeckillCacheUtils;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.math.RandomUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author BJ
 * @date 2022/11/8 20:47
 */
@Service
@Slf4j
public class SeckillSpuServiceImpl implements ISeckillSpuService {
    @Autowired
    private SeckillSpuMapper spuMapper;

    @DubboReference
    private IForSeckillSpuService dubboSeckillSpuService;
    @Autowired
    private RedisTemplate redisTemplate;
    //当前项目中没有定义 spuDetail 对应的Redis的key的常量
    private static final String SECKILL_SPU_DETAIL_VO_PREFIX = "seckill:spu:detail:vo:";


    // 分页查询秒杀的spu商品
//    包含spu所有属性同时
//    包含start_time end_time 和 展示秒杀价格  【秒杀信息】+【普通信息】
    @Override
    public JsonPage<SeckillSpuVO> listSeckillSpus(Integer page, Integer pageSize) {
        PageHelper.startPage(page,pageSize);
        List<SeckillSpu> seckillSpus = spuMapper.findSeckillSpus();
        List<SeckillSpuVO> seckillSpuVOS = new ArrayList<>();
        // 【遍历查到的所有spu商品得到spuId】
        for (SeckillSpu seckillSpu : seckillSpus) {
            Long spuId = seckillSpu.getSpuId();
            // 【通过spuId查询spu商品的标准信息】
            SpuStandardVO standardVO = dubboSeckillSpuService.getSpuById(spuId);
            SeckillSpuVO seckillSpuVO = new SeckillSpuVO();
            // 【进行属性赋值】
            BeanUtils.copyProperties(standardVO,seckillSpuVO);
            seckillSpuVO.setStartTime(seckillSpu.getStartTime());
            seckillSpuVO.setEndTime(seckillSpu.getEndTime());
            seckillSpuVO.setSeckillListPrice(seckillSpu.getListPrice());

            seckillSpuVOS.add(seckillSpuVO);
        }

        return JsonPage.restPage(new PageInfo<>(seckillSpuVOS));
    }

    // 返回值 包含spu所有属性同时
    // 包含start_time end_time 和 展示秒杀价格
    // 策略：从数据库中查询出常规信息+秒杀信息
    @Override
    public SeckillSpuVO getSeckillSpu(Long spuId) {
        // 此处为布隆过滤器判断

        // 1.先确定在Redis中的key
        String seckillSpuKey = SeckillCacheUtils.getSeckillSpuVOKey(spuId);
        SeckillSpuVO seckillSpuVO = null;
        if (redisTemplate.hasKey(seckillSpuKey)) {
            // 2.如果存在这个key直接返回给根据这个key查出来的数据
            seckillSpuVO = (SeckillSpuVO) redisTemplate.boundValueOps(seckillSpuKey).get();
        }else {
            // 3.如果不存在这个key
            // 3.1 查询出秒杀数据
            SeckillSpu seckillSpu = spuMapper.findSeckillSpuBySpuId(spuId);
            if (seckillSpu == null){ // 判断的原因为布隆过滤器可能误判
                throw new CoolSharkServiceException(ResponseCode.NOT_FOUND,"您访问的商品不存在！");
            }
            // 3.2 查询常规信息
            SpuStandardVO spuStandardVO = dubboSeckillSpuService.getSpuById(spuId);
            seckillSpuVO = new SeckillSpuVO();
            // 同名属性赋值
            BeanUtils.copyProperties(spuStandardVO,seckillSpuVO);
            // 单独赋值
            seckillSpuVO.setStartTime(seckillSpu.getStartTime());
            seckillSpuVO.setEndTime(seckillSpu.getEndTime());
            seckillSpuVO.setSeckillListPrice(seckillSpu.getListPrice());
            // 3.3 将赋满值的seckillSpuVO（不包括url） 保存在Redis中
            redisTemplate.boundValueOps(seckillSpuKey).set(seckillSpuVO,
                    10*60*1000+RandomUtils.nextInt(10000),TimeUnit.MILLISECONDS);
        }
        // seckillSpuVO 中url属性中是否有随机码，关乎用户能不能下单成功！
        // 判断当前时间是否在秒杀时间范围内，不再则不给url赋值
        LocalDateTime nowTime = LocalDateTime.now();
        if (seckillSpuVO.getStartTime().isBefore(nowTime) && nowTime.isBefore(seckillSpuVO.getEndTime())){
            // 4.1 得到key
            String randCodeKey = SeckillCacheUtils.getRandCodeKey(spuId);
            // 4.2 得到随机码
            String randCode = redisTemplate.boundValueOps(randCodeKey).get().toString();
            seckillSpuVO.setUrl("/seckill/"+randCode);
        }
        return seckillSpuVO;
    }

    // 根据spuId从Redis中查询秒杀Spu商品详情信息
    @Override
    public SeckillSpuDetailSimpleVO getSeckillSpuDetail(Long spuId) {
        // 1.得到 当前要查询的秒杀商品SPU详情VO 的key
        String seckillSpuDetailKey = SECKILL_SPU_DETAIL_VO_PREFIX + spuId;
        SeckillSpuDetailSimpleVO SpuDetailVO = null;
        if (redisTemplate.hasKey(seckillSpuDetailKey)) {
            // 2.如果当前key对应的spu商品详情信息存在，就直接返回
            SpuDetailVO = (SeckillSpuDetailSimpleVO) redisTemplate.boundValueOps(seckillSpuDetailKey).get();
        } else {
            // 3.当前key对应的值不存在，就从数据库中查询
            SpuDetailStandardVO detailStandardVO = dubboSeckillSpuService.getSpuDetailById(spuId);
            SpuDetailVO = new SeckillSpuDetailSimpleVO();
            BeanUtils.copyProperties(detailStandardVO, SpuDetailVO);
            // 4.将数据库中查询到的spu商品详情信息保存到redis中
            redisTemplate.boundValueOps(seckillSpuDetailKey).set(SpuDetailVO,
                    125 * 60 * 1000 + RandomUtils.nextInt(10000), TimeUnit.MILLISECONDS);
        }
        // 5.返回给前端从Redis中查询到的要秒杀的spu商品详情信息
        return SpuDetailVO;
    }
}
